//==============================================================================
//
// DBigFraction - the arbitrary length fraction number class in the ofc-library
//
//               Copyright (C) 2006  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2006-07-22 13:28:55 $ $Revision: 1.6 $
//
//==============================================================================

#include "ofc/DBigFraction.h"

#ifdef HAVE_DBIGFRACTION
#include <gmp.h>
#endif

#include <ctype.h>


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DTextable.h"
#include "ofc/DParsable.h"
#include "ofc/DSimpleArithmetic.h"
#include "ofc/DComparable.h"
#include "ofc/DText.h"

#if defined(HAVE_LIBGMP) && defined(HAVE_GMP_H)
#define HAVE_DBIGFRACTION  1
#endif

#ifdef HAVE_DBIGFRACTION

//
// The big fraction class implements a number of methods for working with 
// arbitrary large fraction numbers, also called rational numbers.
// The class uses the excellent gmp library for the actual calculations.
// 

@interface DBigFraction : Object <DTextable,DParsable,DSimpleArithmetic,DComparable>
{
@private
  void        *_value;      // the big fraction value
}
#endif
#endif



#ifdef HAVE_DBIGFRACTION


@implementation DBigFraction

  
#if _PRIVATE_

extern void _gmpInit();   // defined in DBigInt

#endif


//// Constructors

//
// Initialise to fraction zero
//
// @return the object
//

- (DBigFraction *) init
{
  [super init];

  _gmpInit();
  
  _value = objc_malloc(sizeof(mpq_t));
  
  mpq_init((mpq_ptr) _value);
  
  return self;
}

//
// Initialise the fraction with a double
// 
// @param value    the double value
// 
// @return the object
// 
- (DBigFraction *) init :(double) value
{
  [self init];
  
  [self set :value];
  
  return self;
}

//
// Initialise the fraction with a text string
// 
// @param cstring  the cstring with the fraction
// @param base     the base for the fraction
// 
// @return the object
// 
- (DBigFraction *) init :(const char *) cstring :(int) base
{
  [self init];
  
  [self set :cstring :base];
  
  return self;
}


//// Copy constructor

//
// Do a shallow copy of the big fraction
//
// @return the object
//
- shallowCopy
{
  DBigFraction *copy = [super shallowCopy];
  
  copy->_value  = objc_malloc(sizeof(mpq_t));
  
  mpq_init((mpq_ptr) copy->_value);
  
  mpq_set ((mpq_ptr) copy->_value, _value);
  
  return copy;
}


//// Deconstructor

//
// Free a big fraction
// 
// @return the object
//
- free
{
  mpq_clear((mpq_ptr) _value);
  
  objc_free(_value); _value = nil;
  
  return [super free];
}


//// Set methods

//
// Set the big fraction to zero
// 
// @return the object
// 
- (DBigFraction *) clear
{
  mpq_set_ui((mpq_ptr) _value, 0, 1);

  return self;
}

//
// Set the big fraction with a double
// 
// @param value    the double value
// 
// @return the object
// 
- (DBigFraction *) set :(double) value
{
  mpq_set_d((mpq_ptr) _value, value);
  
  return self;
}

//
// Set the big fraction with a text string
//
// @param cstring   the cstring with the fraction
// @param base      the base of the number (2..36)
//
// @return success
//
- (BOOL) set :(const char *) cstring :(int) base
{
  BOOL ok = NO;
  
  if ((base < 2) || (base > 36))
  {
    WARNING(DW_INVALID_ARG, "base");
  }
  else
  {
    ok = (mpq_set_str((mpq_ptr) _value, cstring, base) != -1);
    
    [self norm];
  }

  return ok;
}

//
// Set the big fraction with (small) numerator and denominator 
//
// @param num      the numerator
// @param denom    the denominator
//
// @return the object
//

- (DBigFraction *) fraction :(int) num :(int) denom
{
  if (denom != 0)
  {
    if (denom < 0)
    {
      num   = -num;
      denom = -denom;
    }
    
    mpq_set_si((mpq_ptr) _value, (long) num, (unsigned long) denom);
    
    [self norm];
  }
  else
  {
    WARNING(DW_INVALID_ARG, "denom");
  }
  
  return self;
}

//
//
// Move an other object in this object
//
// @param other    the other object
//
// @return this object
//
- (DBigFraction *) move :(DBigFraction *) other
{
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    mpq_set((mpq_ptr) _value, (mpq_ptr) other->_value);
  }
  
  return self;
}


//// Get methods

//
// Get the big fraction as a double
// 
// @return the double
// 
- (double) get
{
  return mpq_get_d((mpq_ptr) _value);
}

//
// Convert to a double
//
// @return the double
//
- (double) toDouble
{
  return (mpq_get_d((mpq_ptr) _value));
}

//
// Get the big fraction as a text string
// 
// @param base     the base for the text string (2..36)
// 
// @return a (new) text string (or nil)
// 
- (DText *) get :(int) base
{
  DText *str = nil;
  
  if ((base < 2) || (base > 36))
  {
    WARNING(DW_INVALID_ARG, "base");
  }
  else
  {
    char *out = mpq_get_str(NULL, base, (mpq_ptr) _value);
    
    if (out != NULL)
    {
      str = [DText new];
      
      [str set :out];
      
      objc_free(out);
    }
  }
  
  return str;
}

//
// Get the numerator (as small integer)
//
// @return the numerator
//
- (long) numerator
{
  return (mpz_get_si(mpq_numref((mpq_ptr) _value)));
}

//
// Get the denominator (as small integer)
//
// @return the denominator
//
- (long) denominator
{
  return (mpz_get_si(mpq_denref((mpq_ptr) _value)));
}
  

//// Methods on big fraction numbers

//
// Add with an other big fraction
//
// @param other    the big fraction
//
// @return the object
//
- (DBigFraction *) add :(DBigFraction *) other
{
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else 
  {
    mpq_add((mpq_ptr) _value, (mpq_ptr) _value, (mpq_ptr) other->_value);
  }
  
  return self;
}

//
// Add two big fractions and store the result in the object
//
// @param src1     the first big fraction
// @param src2     the second big fraction
//
// @return the object
//

- (DBigFraction *) add :(DBigFraction *) src1 :(DBigFraction *) src2
{
  if ((src1 == nil) || (src2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "src?");
  }
  else 
  {
    mpq_add((mpq_ptr) _value, (mpq_ptr) src1->_value, (mpq_ptr) src2->_value);
  }
  
  return self;
}

//
// Subract with an other big fraction
//
// @param other     the big fraction
//
// @return the object
//

- (DBigFraction *) sub :(DBigFraction *) other
{
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    mpq_sub((mpq_ptr) _value, (mpq_ptr) _value, (mpq_ptr) other->_value);
  }

  return self;
}

//
// Subract two big fractions and store the result in the object
//
// @param src1      the first big fraction
// @param src2      the second big fraction
//
// @return the object
//

- (DBigFraction *) sub :(DBigFraction *) src1 :(DBigFraction *) src2
{
  if ((src1 == nil) || (src2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "src?");
  }
  else
  {
    mpq_sub((mpq_ptr) _value, (mpq_ptr) src1->_value, (mpq_ptr) src2->_value);
  }

  return self;
}

//
// Multiply with an other big fraction
//
// @param other    the big fraction
//
// @return the object
//

- (DBigFraction *) mul :(DBigFraction *) other
{ 
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    mpq_mul((mpq_ptr) _value, (mpq_ptr) _value, (mpq_ptr) other->_value);
  }
  
  return self;
}

//
// Multiply two big fractions and store the result in the object
//
// @param src1    the first big fraction
// @param src2    the second big fraction
//
// @return the object
//

- (DBigFraction *) mul :(DBigFraction *) src1 :(DBigFraction *) src2
{ 
  if ((src1 == nil) || (src2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "src?");
  }
  else
  {
    mpq_mul((mpq_ptr) _value, (mpq_ptr) src1->_value, (mpq_ptr) src2->_value);
  }
  
  return self;
}

//
// Divide with an other big fraction
//
// @param other    the divisor big fraction
//
// @return the object
//

- (DBigFraction *) div :(DBigFraction *) other
{
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    mpq_div((mpq_ptr) _value, (mpq_ptr) _value, (mpq_ptr) other->_value);
  }
  
  return self; 
}

//
// Divide two big fractions and store the result in the object
//
// @param src1    the dividend big fraction
// @param src2    the divisor big fraction
//
// @return the object
//

- (DBigFraction *) div :(DBigFraction *) src1 :(DBigFraction *) src2
{
  if ((src1 == nil) || (src2 == nil))
  {
    WARNING(DW_NIL_NOT_ALLOWED, "src?");
  }
  else
  {
    mpq_div((mpq_ptr) _value, (mpq_ptr) src1->_value, (mpq_ptr) src2->_value);
  }
  
  return self; 
}

//
// Absolute the big fraction
// 
// @return the object
// 

- (DBigFraction *) abs
{
  mpq_abs((mpq_ptr) _value, (mpq_ptr) _value);
  
  return self;
}

//
// Negate the big fraction
// 
// @return the object
// 
- (DBigFraction *) negate
{
  mpq_neg((mpq_ptr) _value, (mpq_ptr) _value);
  
  return self;
}

//
// Invert the big fraction
// 
// @return the object
// 
- (DBigFraction *) invert
{
  mpq_inv((mpq_ptr) _value, (mpq_ptr) _value);

  return self;
}

//
// Normalize the big fraction
//
// @return the object
//
- (DBigFraction *) norm
{
  mpq_canonicalize((mpq_ptr) _value);
  
  return self;
}


//// DComparable protocol implementation

//
// Compare with an other big fraction
//
// @param other    the other big fraction
// 
// @return the compare result ( <0, 0, 0> )
//

- (int) compare :(DBigFraction *) other
{
  int result = 0;
  
  if (other == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "other");
  }
  else
  {
    result = mpq_cmp((mpq_ptr) _value, (mpq_ptr) other->_value);
  }
  
  return result;
}


//// Textable protocol implementation

//
// Convert to a decimal text string
//
// @return a (new) text string with the big fraction (or nil)
//

- (DText *) toText
{
  return [self get :10];
}


//// DParsable protocol implementation

#if _PRIVATE_
//
// Check for octal number
//
// @param ch     the character
// 
// @return is it an octal number ?
//
static BOOL _isoct(char ch)
{
  return ((ch >= '0') && (ch <= '7'));
}

//
// Scan a number
//
// @param pntr     the pointer in the string
// @param output   the output string
// 
// @return the updated pointer in the string
//
static char *_scanNumber(char *pntr, DText *output)
{
  BOOL hex = NO;
  BOOL dec = NO;
  BOOL oct = NO;
  
  if (*pntr == '0') // process base indicator
  {
    [output push :*pntr++];
    
    if (tolower(*pntr) == 'x')
    {
      [output push :*pntr++];
      
      hex = YES;
    }
    else
    {
      oct = YES;
    }
  }
  else if (isdigit(*pntr))
  {
    dec = YES;
  }
  
  while ( ((isxdigit(*pntr)) && (hex)) || // scan the number
          ((isdigit (*pntr)) && (dec)) ||
          ((_isoct  (*pntr)) && (oct)) )
  {
    [output push :*pntr++];
  }
  
  return pntr;
}
#endif

//
// Parse a string for a big fraction. Format: [space]*[+-]number/number
// and number is one of:
// decimal:     [1..9][0..9]*
// hexadecimal: 0x[0..9a..f]*
// octal:       0[0..7]*
// 
// @param cstr     the string to be parsed (moved to the first non-parsable char)
// 
// @return the result (0,ERANGE,ENODATA)
// 
- (int) fromString :(char **) cstr
{
  DText *str    = [DText new];
  char  *pntr   = *cstr;
  char  *start  = pntr;
  int    result = ENODATA;
  
  while (isspace(*pntr)) // skip spaces
  {
    pntr++;
  }

  start = pntr;

  if ((*pntr == '+') || (*pntr == '-')) // process sign
  {
    [str push :*pntr++];
  }
  
  pntr = _scanNumber(pntr, str);
  
  if (*pntr == '/')
  {
    [str push :*pntr++];
    
    pntr = _scanNumber(pntr, str);
  }
  
  if (pntr != start)
  {
    if (mpq_set_str((mpq_ptr) _value, [str cstring], 0) != -1)
    {
      [self norm];
      
      result = 0;
      
      *cstr = pntr;
    }
    else
    {
      result = ERANGE;
    }
  }
  
  [str free];
  
  return result;
}

@end

#endif
  
/*===========================================================================*/

